using System;
using System.Collections;
using System.Collections.Generic;

namespace Jonathan.SharpDB
{
	/// <summary>
	/// A Query operation for SharpDB.
	/// </summary>
    public class Query
    {
        #region - Variables -

        /// <summary>
        /// The name of the table...
        /// </summary>
        private string m_Table;

        /// <summary>
        /// The type of Query...
        /// </summary>
        private QueryType m_Type;

        /// <summary>
        /// The modified columns...
        /// </summary>
        private List<string> m_ModCols;

        /// <summary>
        /// The modified values...
        /// </summary>
        private ArrayList m_ModVals;

        /// <summary>
        /// Where columns...
        /// </summary>
        private List<string> m_Columns;

        /// <summary>
        /// Where operators ('=', '!=', ...)...
        /// </summary>
        private List<Operator> m_Operators;

        /// <summary>
        /// Where Values...
        /// </summary>
        private ArrayList m_Values;

        /// <summary>
        /// The amount of Rows affected by the query...
        /// </summary>
        private int m_RowsAffected;

        /// <summary>
        /// Results of the Query...
        /// </summary>
        private Results m_Results;

        /// <summary>
        /// The Order By Column...
        /// </summary>
        private string m_OrderBy;

        /// <summary>
        /// Direction to order Results...
        /// </summary>
        private SortDirection m_OrderDir;

        /// <summary>
        /// The Group By Column...
        /// </summary>
        private string m_GroupBy;

        #endregion

        #region - Constructors -

        /// <summary>
        /// Creates a new Query...
        /// </summary>
        /// <param name="type">Type of Query.</param>
        public Query(QueryType type) : this(type, null)
        {
        }

        /// <summary>
        /// Creates a new Query
        /// </summary>
        /// <param name="type">Type of Query.</param>
        /// <param name="tableName">Name of Table for Query.</param>
        public Query(QueryType type, string tableName)
        {
            this.m_OrderDir = SortDirection.ASCENDING;
            this.m_GroupBy = "";
            this.m_OrderBy = "";
            this.m_Type = type;
			this.m_Columns = new List<string>();
            this.m_Values = new ArrayList();
            this.m_Operators = new List<Operator>();
            this.m_ModCols = new List<string>();
            this.m_ModVals = new ArrayList();
            this.m_Table = tableName;
            this.m_RowsAffected = 0;
        }

        #endregion

        #region - Properties -

        /// <summary>
        /// Gets or Sets the Name of the Table for the Query...
        /// </summary>
        public string Table
        {
            get { return this.m_Table; }

            set { this.m_Table = value; }
        }

        /// <summary>
        /// Gets or Sets the Type of Query to perform...
        /// </summary>
        public QueryType QueryType
        {
            get { return this.m_Type; }

            set 
            { 
                this.m_Type = value;
                this.m_Operators.Clear();
                this.m_Columns.Clear();
                this.m_Values.Clear();
            }
        }

        /// <summary>
        /// Gets the Rows Affected by the Query...
        /// </summary>
        public int RowsAffected
        {
            get { return this.m_RowsAffected; }
        }

        /// <summary>
        /// Gets the Rows Returned by the Query...
        /// </summary>
        public int RowsReturned
        {
            get 
            {
                if (this.m_Results != null)
                {
                    return this.m_Results.RowCount;
                }
                else
                {
                    return -1;
                }
            }
        }

        /// <summary>
        /// Gets the Results of the Query...
        /// </summary>
        public Results Results
        {
            get { return this.m_Results; }
        }

        /// <summary>
        /// Gets or Sets the Column to Group the results by...
        /// </summary>
        public string GROUPBY
        {
            get { return this.m_GroupBy; }

            set { this.m_GroupBy = value.Trim(); }
        }

        /// <summary>
        /// Gets the List of Columns for the Query...
        /// </summary>
        internal List<string> Columns
        {
            get { return this.m_Columns; }
        }

        /// <summary>
        /// Gets the List of Values for the Query...
        /// </summary>
        internal ArrayList Values
        {
            get { return this.m_Values; }
        }

        /// <summary>
        /// Gets the List of Operators for the Query...
        /// </summary>
        internal List<Operator> Operators
        {
            get { return this.m_Operators; }
        }

        /// <summary>
        /// Gets the List of Modified Values for the Query...
        /// </summary>
        internal ArrayList ModValues
        {
            get { return this.m_ModVals; }
        }

        /// <summary>
        /// Gets the List of Modified Columns for the Query...
        /// </summary>
        internal List<string> ModColumns
        {
            get { return this.m_ModCols; }
        }

        #endregion

        #region - Create Column -

        /// <summary>
        /// Add a column for the create query...
        /// </summary>
        /// <param name="column">Name of Column.</param>
        /// <returns>DBMessage</returns>
        public DBMessage AddCreateColumn(string column)
        {
            return this.AddCreateColumn(column, ColumnType.STRING);
        }

        /// <summary>
        /// Add a column for the create query...
        /// </summary>
        /// <param name="column">Name of Column.</param>
        /// <param name="columnType">Type of Column.</param>
        /// <returns>DBMessage</returns>
        public DBMessage AddCreateColumn(string column, ColumnType columnType)
        {
            if (this.m_Type == QueryType.CREATE)
            {
                this.m_Columns.Add(column);
                this.m_Values.Add(columnType);

                return DBMessage.SUCCESS;
            }
            else
            {
                return DBMessage.INCORRECT_QUERY_TYPE;
            }
        }

        #endregion

        #region - Insert -

        /// <summary>
        /// Adds a column with the given value to the query...
        /// </summary>
        /// <param name="column">Column name.</param>
        /// <param name="value">Value of new Column.</param>
        /// <returns>DBMessage</returns>
        public DBMessage INSERTVALUE(string column, object value)
        {
            if (this.m_Type == QueryType.INSERT)
            {
                this.m_Columns.Add(column.Trim());
                this.m_Values.Add(value);

                return DBMessage.SUCCESS;
            }
            else
            {
                return DBMessage.INCORRECT_QUERY_TYPE;
            }
        }

        #endregion

        #region - Select -

        /// <summary>
        /// Adds a column to select in the query...
        /// </summary>
        /// <param name="column">Name of Column.</param>
        /// <returns>DBMessage</returns>
        public DBMessage SELECT(string column)
        {
            if (this.m_Type == QueryType.SELECT)
            {
				if (this.m_ModCols.Contains(column))
					return DBMessage.COLUMN_IN_QUERY;

                this.m_ModCols.Add(column.Trim());
                return DBMessage.SUCCESS;
            }
            else
            {
                return DBMessage.INCORRECT_QUERY_TYPE;
            }
        }

        #endregion

        #region - Set -

        /// <summary>
        /// Adds a set column = value to the Query...
        /// </summary>
        /// <param name="column">Column Name.</param>
        /// <param name="value">New Value.</param>
        /// <returns>DBMessage</returns>
        public DBMessage SET(string column, object value)
        {
            if (this.m_Type == QueryType.UPDATE)
            {
                this.m_ModCols.Add(column.Trim());
                this.m_ModVals.Add(value);

                return DBMessage.SUCCESS;
            }
            else
            {
                return DBMessage.INCORRECT_QUERY_TYPE;
            }
        }

        #endregion

        #region - Where -

        /// <summary>
        /// Adds a column and value to the delete query...
        /// </summary>
        /// <param name="column">Column Name.</param>
        /// <param name="opType">Operator Type ('=', '!=', ...).</param>
        /// <param name="value">Value to look for.</param>
        /// <returns>DBMessage</returns>
        public DBMessage WHERE(string column, Operator opType, object value)
        {
            if (this.m_Type == QueryType.DELETE ||
                this.m_Type == QueryType.UPDATE ||
                this.m_Type == QueryType.SELECT )
            {
                this.m_Columns.Add(column.Trim());
                this.m_Operators.Add(opType);
                this.m_Values.Add(value);

                return DBMessage.SUCCESS;
            }
            else
            {
                return DBMessage.INCORRECT_QUERY_TYPE;
            }
        }

        #endregion

        #region - Order By -

        /// <summary>
        /// Sets the Column to Order the Results by...
        /// </summary>
        public void ORDERBY(string colName, SortDirection dir)
        {
            this.m_OrderBy = colName.Trim();
            this.m_OrderDir = dir;
        }

        /// <summary>
        /// Gets the Column to Sort Results by...
        /// </summary>
        public string OrderByCol
        {
            get { return this.m_OrderBy; }
        }

        /// <summary>
        /// Gets the Direction to order the reults...
        /// </summary>
        public SortDirection OrderDir
        {
            get { return this.m_OrderDir; }
        }

        #endregion

        #region - Check Column -

        /// <summary>
        /// Checks to see if the given column is in the query already...
        /// </summary>
        /// <param name="column">Name of Column.</param>
        /// <returns>True is column is in query.</returns>
        private bool CheckColumn(string column)
        {
            for (int a = 0; a < this.m_Columns.Count; a++)
            {
                if (this.m_Columns[a].Equals(column, StringComparison.CurrentCultureIgnoreCase))
                {
                    return true;
                }
            }

            return false;
        }

        #endregion

        #region - Set Results -

        /// <summary>
        /// Sets the Query results...
        /// </summary>
        /// <param name="r">Query Results.</param>
        internal void SetResults(Results r)
        {
            this.m_Results = r;
        }

        /// <summary>
        /// Sets the Rows Affected...
        /// </summary>
        /// <param name="r">Number of Rows Affected.</param>
        internal void SetRowsAffected(int r)
        {
            this.m_RowsAffected = r;
        }

        #endregion
    }
}
